# The MIT License
#
# Copyright (c) 2007 Leah Culver
# Copyright (c) 2009 Canonical, Ltd.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
'''
OAuth common interface
'''
import binascii
import cgi
import hmac
import urllib

# For compatibility
from poauth import client
OAuthRequest = client.OAuthRequest
OAuthClient = client.OAuthClient
escape = client.escape

OAUTH_VERSION = '1.0'


class OAuthConsumer(object):
    '''
    OAuthConsumer is a simple data type for storing consumer in memory.
    '''
    def __init__(self, key, secret):
        self.key = key
        self.secret = secret

class OAuthToken(object):
    '''
    OAuthToken is the data type for storing the token in memory.
    '''
    def __init__(self, key, secret, *args, **kwargs):
        self.key = key
        self.secret = secret
        self.verifier = kwargs.get('verifier', None)
        self.callback = kwargs.get('callback', None)

    def to_string(self):
        '''
        Returns a form encoded string with the token and secret
        '''
        return urllib.urlencode({'oauth_token': self.key,
                                 'oauth_token_secret': self.secret})
 
    @staticmethod
    def from_string(s):
        '''
        Returns an OAuthToken from a token query string:
        oauth_token_secret=xxx&oauth_token=xxx
        '''
        params = cgi.parse_qs(s, keep_blank_values=False)
        key = params['oauth_token'][0]
        secret = params['oauth_token_secret'][0]
        verifier = params.get('oauth_verifier', [None])[0]
        verifier = params.get('oauth_callback', [None])[0]
        return OAuthToken(key, secret)

    def __str__(self):
        return self.to_string()


class OAuthSignatureMethod(object):
    '''Base class for OAuth signature methods.'''

    def get_name(self):
        """-> str."""
        raise NotImplementedError

    def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
        """-> str key, str raw."""
        raise NotImplementedError

    def build_signature(self, oauth_request, oauth_consumer, oauth_token):
        """-> str."""
        raise NotImplementedError

    def check_signature(self, oauth_request, consumer, token, signature):
        built = self.build_signature(oauth_request, consumer, token)
        return built == signature


class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
    '''HMAC-SHA1 Signature method implementation'''

    def get_name(self):
        return 'HMAC-SHA1'
        
    def build_signature_base_string(self, oauth_request, consumer, token):
        sig = (
            escape(oauth_request.get_normalized_http_method()),
            escape(oauth_request.get_normalized_http_url()),
            escape(oauth_request.get_normalized_parameters()),
        )

        key = '%s&' % escape(consumer.secret)
        if token:
            key += escape(token.secret)
        raw = '&'.join(sig)
        return key, raw

    def build_signature(self, oauth_request, consumer, token):
        """Builds the base signature string."""
        key, raw = self.build_signature_base_string(oauth_request, consumer,
            token)

        # HMAC object.
        try:
            import hashlib # 2.5
            hashed = hmac.new(key, raw, hashlib.sha1)
        except:
            import sha # Deprecated
            hashed = hmac.new(key, raw, sha)

        # Calculate the digest base 64.
        return binascii.b2a_base64(hashed.digest())[:-1]


class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
    '''PLAINTEXT Signature method implementation'''

    def get_name(self):
        return 'PLAINTEXT'

    def build_signature_base_string(self, oauth_request, consumer, token):
        """Concatenates the consumer key and secret."""
        sig = '%s&' % escape(consumer.secret)
        if token:
            sig = sig + escape(token.secret)
        return sig, sig

    def build_signature(self, oauth_request, consumer, token):
        key, raw = self.build_signature_base_string(oauth_request, consumer,
            token)
        return key


class OAuthSignatureMethod_RSA_SHA1(OAuthSignatureMethod):
    '''RSA-SHA1 Signature method implementation'''

    def get_name(self):
        return 'RSA-SHA1'

    def build_signature_base_string(self, request, consumer, token):
        raise NotImplementedError

    def build_signature(self, request, consumer, token):
        raise NotImplementedError
